package com.googlecode.cswish.struts.spring;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import net.sf.ehcache.Element;
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.search.expression.BaseCriteria;

import org.apache.struts2.StrutsStatics;

import com.googlecode.cswish.security.Context;
import com.googlecode.cswish.security.SafeManager;
import com.googlecode.cswish.security.auth.UserInfo;
import com.googlecode.cswish.security.type.RecordRuleSetOr;
import com.googlecode.cswish.util.Functions;
import com.opensymphony.xwork2.ActionContext;

public class RecordPermissionCriteria extends BaseCriteria {

	private boolean alwaysTrue;
	
	private Context context;
	
	private RecordRuleSetOr recordRuleSetOr;
	
	public RecordPermissionCriteria(Context context){
		this.context = context;
	}
	
	public RecordPermissionCriteria(SafeManager safeManager, Functions functions, 
			String modelName, String method) {
		ActionContext actionContext =  ActionContext.getContext();
    	if (actionContext == null) {
    		alwaysTrue = true;
    		return ;
    	}
    	HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
    	UserInfo userInfo = safeManager.getLoginUser(request);
    	if (userInfo == null) {
    		alwaysTrue = true;
    		return ;
    	}
    	
    	String path = functions.linkModelNoBase(modelName, method, true);
    	context = new Context(actionContext.getValueStack(), functions);
    	context.setUserInfo(userInfo);
    	recordRuleSetOr = safeManager.getRecordLimitPermission(userInfo.getLoginId(), path, context);
    	if (recordRuleSetOr == null || RecordRuleSetOr.FALSE.equals(recordRuleSetOr)) {
    		alwaysTrue = true;
    	} else {
    		alwaysTrue = false;
    	}
	}
	
	public boolean isAlwaysTrue() {
		return alwaysTrue;
	}

	public RecordRuleSetOr getRecordRuleSetOr() {
		return recordRuleSetOr;
	}

	public void setRecordRuleSetOr(RecordRuleSetOr recordRuleSetOr) {
		this.recordRuleSetOr = recordRuleSetOr;
	}

	@Override
	public boolean execute(Element element,
			Map<String, AttributeExtractor> attributeExtractors) {
		if (alwaysTrue) {
			return true;
		} else {			
			Object model = new MockMap(element, attributeExtractors);
			context.setModel(model);
			return recordRuleSetOr.executeBoolean(context);
		}
	}
	
	class MockMap implements Map<String, Object> {
		private Map<String, AttributeExtractor> attributeExtractors;
		private Element element;
		
		MockMap(Element element, Map<String, AttributeExtractor> attributeExtractors) {
			this.element = element;
			this.attributeExtractors = attributeExtractors;
		}

		@Override
		public int size() {
			return attributeExtractors.size();
		}

		@Override
		public boolean isEmpty() {
			return attributeExtractors.isEmpty();
		}

		@Override
		public boolean containsKey(Object key) {
			return attributeExtractors.containsKey(key);
		}

		@Override
		public boolean containsValue(Object value) {
			throw new UnsupportedOperationException("containsValue");
		}

		@Override
		public Object get(Object key) {
			return attributeExtractors.get(key).attributeFor(element, (String)key);
		}

		@Override
		public Object put(String key, Object value) {
			throw new UnsupportedOperationException("put");
		}

		@Override
		public Object remove(Object key) {
			throw new UnsupportedOperationException("remove");
		}

		@Override
		public void putAll(Map<? extends String, ? extends Object> m) {
			throw new UnsupportedOperationException("putAll");
		}

		@Override
		public void clear() {
			throw new UnsupportedOperationException("clear");
		}

		@Override
		public Set<String> keySet() {
			return attributeExtractors.keySet();
		}

		@Override
		public Collection<Object> values() {
			throw new UnsupportedOperationException();
		}

		@Override
		public Set<java.util.Map.Entry<String, Object>> entrySet() {
			int initialCapacity = attributeExtractors.size() * 4 / 3;
			Set<Entry<String, Object>> entrySet = new HashSet<Map.Entry<String,Object>>(initialCapacity);
			for (Entry<String, AttributeExtractor> entry : attributeExtractors.entrySet()) {
				entrySet.add(new EntryMock(entry.getKey()));
			}
			return entrySet;
		}
		
		public class EntryMock implements Entry<String, Object> {

			private String key;
			
			public EntryMock(String key) {
				this.key = key;
			}
			
			@Override
			public String getKey() {
				return key;
			}

			@Override
			public Object getValue() {
				return attributeExtractors.get(key).attributeFor(element, (String)key);
			}

			@Override
			public Object setValue(Object value) {
				throw new UnsupportedOperationException("setValue");
			}
		}
	}
}
